package com.sam4cloud.oauth2.project.service.impl;

import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sam4cloud.common.core.cons.HttpCode;
import com.sam4cloud.common.core.enums.StatusEnum;
import com.sam4cloud.common.core.utils.AssertUtil;
import com.sam4cloud.common.core.utils.LocalDateTimeUtils;
import com.sam4cloud.common.redis.utils.RedisUtils;
import com.sam4cloud.oauth2.project.cons.AuthCons;
import com.sam4cloud.oauth2.project.mapper.OauthUserMapper;
import com.sam4cloud.oauth2.project.model.dto.LoginDTO;
import com.sam4cloud.oauth2.project.model.entity.OauthUser;
import com.sam4cloud.oauth2.project.model.vo.LoginUser;
import com.sam4cloud.oauth2.project.service.IOauthUserService;
import com.sam4cloud.oauth2.utils.EncryptUtils;
import com.sam4cloud.oauth2.utils.RSAUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author Sam
 * @since 2022-11-14
 */
@Service
public class OauthUserServiceImpl extends ServiceImpl<OauthUserMapper, OauthUser> implements IOauthUserService {

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public LoginUser checkLogin(LoginDTO loginDTO) {
        String loginName = loginDTO.getLoginName();
        String password = loginDTO.getPassword();
        String code = loginDTO.getCode();

        //rsa解密后的密码
        //String decryptPwd = RSAUtils.decryptStr(password);
        String decryptPwd = password;

        //校验验证码
        String sourceCode = redisUtils.get(AuthCons.LOGIN_VERIFY_CODE + loginName);
        AssertUtil.isEmpty(sourceCode, HttpCode.SC_FORBIDDEN, "验证码已失效，请重新获取");
        AssertUtil.isFalse(sourceCode.equals(code), HttpCode.SC_FORBIDDEN, "验证码错误");

        //校验用户是否被锁定
        String userFrozen = redisUtils.get(AuthCons.LOGIN_USER_LOCKED + loginName);
        AssertUtil.isNotEmpty(userFrozen, HttpCode.SC_FORBIDDEN, "用户被锁定，还需等待" + userFrozen + "秒");

        //校验用户有效性和是否被禁用
        OauthUser oauthUser = this.getOne(Wrappers.<OauthUser>lambdaQuery().eq(OauthUser::getLoginName, loginName), false);
        AssertUtil.isNull(oauthUser, HttpCode.SC_FORBIDDEN, "用户名或密码错误");
        StatusEnum status = oauthUser.getStatus();
        AssertUtil.isTrue(StatusEnum.DISABLE == status, HttpCode.SC_FORBIDDEN, "用户被禁用，请联系管理员操作");
        //校验密码
        String aesPwd = EncryptUtils.aesEncrypt(decryptPwd);
        String dbPwd = oauthUser.getPassword();
        if (!dbPwd.equals(aesPwd)) {
            //校验错误次数
            Long pwdErrorCount = redisUtils.incrBy(AuthCons.LOGIN_USER_PWD_ERROR_COUNT + loginName, 1);
            //超过错误上限则冻结用户(非最大限制内)
            if (pwdErrorCount >= AuthCons.LOGIN_USER_PWD_ERROR_LIMIT && pwdErrorCount < AuthCons.LOGIN_USER_PWD_ERROR_MAX_LIMIT) {
                redisUtils.setEx(AuthCons.LOGIN_USER_LOCKED + loginName,
                        LocalDateTimeUtils.formatNormal(LocalDateTime.now().plus(AuthCons.LOGIN_USER_LOCKED_TIME, ChronoUnit.MINUTES)),
                        AuthCons.LOGIN_USER_LOCKED_TIME, TimeUnit.MINUTES);
                AssertUtil.failure(HttpCode.SC_FORBIDDEN, "验证错误输错次数达上限,您的账号已被锁定，还需等待" + AuthCons.LOGIN_USER_LOCKED_TIME + "分钟");
            } else if (pwdErrorCount >= AuthCons.LOGIN_USER_PWD_ERROR_MAX_LIMIT) {
                //超过认证错误最大限制，则冻结n时长
                redisUtils.setEx(AuthCons.LOGIN_USER_LOCKED + loginName,
                        LocalDateTimeUtils.formatNormal(LocalDateTime.now().plus(AuthCons.LOGIN_USER_LOCKED_MAX_TIME, ChronoUnit.MINUTES)),
                        AuthCons.LOGIN_USER_LOCKED_MAX_TIME, TimeUnit.MINUTES);
                //todo 记录登录异常日志 锁定ip
                AssertUtil.failure(HttpCode.SC_FORBIDDEN, "验证错误输错次数达今日上限,您的账号已被锁定，还需等待" + AuthCons.LOGIN_USER_LOCKED_MAX_TIME + "分钟");
            }
            AssertUtil.failure(HttpCode.SC_FORBIDDEN, "用户名或密码错误，还剩" + pwdErrorCount + "次");
        } else {
            //删除验证码
            redisUtils.del(AuthCons.LOGIN_VERIFY_CODE + loginName);
            //删除错误次数
            redisUtils.del(AuthCons.LOGIN_USER_PWD_ERROR_COUNT + loginName);
        }
        //todo 记录正常登录日志

        return new LoginUser(oauthUser.getId(), oauthUser.getDeptId(), oauthUser.getRoleId(), oauthUser.getLoginName(), oauthUser.getPassword(), new ArrayList<>());
    }

    public static void main(String[] args) {
        RSA rsa = new RSA(RSAUtils.PRIVATE_KEY, RSAUtils.PUBIC_KEY);
        //获得私钥
        System.out.println("rsa.getPrivateKey() = " + rsa.getPrivateKey());
        System.out.println("rsa.getPrivateKeyBase64() = " + rsa.getPrivateKeyBase64());
        //获得公钥
        System.out.println("rsa.getPublicKey() = " + rsa.getPublicKey());
        System.out.println("rsa.getPublicKeyBase64() = " + rsa.getPublicKeyBase64());

        String encrypt = rsa.encryptBase64("我是一个文本", KeyType.PrivateKey);
        String decrypt = rsa.decryptStr(encrypt, KeyType.PublicKey);
        System.out.println("encrypt = " + encrypt);
        System.out.println("decrypt = " + decrypt);

        String s1 = EncryptUtils.md5SaltEncrypt("123456");
        System.out.println("s1 = " + s1);


    }


}
